package com.fancon.android.emoji;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;

import org.jsoup.Jsoup;
import org.jsoup.examples.HtmlToPlainText;
import org.jsoup.nodes.Document;

import android.content.Context;
import android.content.res.AssetManager;
import android.text.ClipboardManager;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import android.widget.EditText;

/**
 * Loading configuration file for accessing emoji's icon
 * 
 * @author Wazi Armstrong
 * 
 */
public class EmojiUtil {

	public static final int NUM_CATEGORY = 5;
	public static final String[] EMOJI_CATEGORIES = { "smiley", "bell",
			"vehicle", "flower", "number" };// The order is important!
	public static final int SMILEY = 0;
	public static final int BELL = 1;
	public static final int VEHICLE = 2;
	public static final int FLOWER = 3;
	public static final int NUMBER = 4;

	/**
	 * Used for mapping from emoji's string to its icon's name and animation's
	 * folder name
	 */
	public static class EmojiMap {
		public final String code;
		public final String image;
		public final int category;
		public final String animationFolder;

		protected EmojiMap(String code, int category, String image,
				String animationFolder) {
			this.code = code;
			this.category = category;
			this.image = image;
			this.animationFolder = animationFolder;
		}
	}

	/**
	 * Contain error throwed when reading configuration file
	 */
	public static class EmojiConfigurationException extends Exception {

		public EmojiConfigurationException(String string) {
			super(string);
		}

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

	}

	private static final String TAG = "EmojiUtil";

	/**
	 * Store a list of emoji's triples (code-image-animation)
	 */
	private ArrayList<EmojiMap>[] maps;

	String mBasedFolder;
	private EmojiDrawableGetter mImageGetter;
	private AssetManager mManager;
	//private Pattern imgPattern;
	private static EmojiUtil sEmojiUtil;

	@SuppressWarnings("unchecked")
	private EmojiUtil(AssetManager manager, String folder) {
		mManager = manager;
		mBasedFolder = folder;
		maps = new ArrayList[NUM_CATEGORY];
		//imgPattern = Pattern
		//		.compile("<img\\s+src\\s*=\\s*(\"|')\\S{1,2}(\"|')\\s*/?>");
		loadConfiguration();
		mImageGetter = new EmojiDrawableGetter(manager, this);
	}

	public static EmojiUtil getInstance(AssetManager manager, String folder) {

		if (sEmojiUtil == null || !folder.equals(sEmojiUtil.mBasedFolder))
			sEmojiUtil = new EmojiUtil(manager, folder);
		return sEmojiUtil;
	}

	/**
	 * Load emoji_mapping.txt in the provided folder
	 * 
	 * @throws IOException
	 * @throws EmojiConfigurationException
	 */
	private void loadConfiguration() {
		for (int i = 0; i < NUM_CATEGORY; i++) {
			loadEmoijCategory(i);
		}
	}

	private void loadEmoijCategory(int category) {

		ArrayList<EmojiMap> emojiMaps = new ArrayList<EmojiMap>();
		maps[category] = emojiMaps;

		BufferedReader reader;
		try {
			reader = new BufferedReader(new InputStreamReader(
					mManager.open(mBasedFolder + "/"
							+ EMOJI_CATEGORIES[category] + "/map.txt")));
			String line;
			String[] words;
			Log.v(TAG, "Loading mapping file for " + EMOJI_CATEGORIES[category]
					+ " category...");
			while ((line = reader.readLine()) != null) {

				words = line.split("\t");
				if (words.length == 2) {
					if (isImageName(words[1])) {
						emojiMaps.add(new EmojiMap(words[0], category,
								words[1], null));
						Log.v(TAG + ".loadConfiguration", words[0] + " "
								+ EMOJI_CATEGORIES[category] + " " + words[1]);
					} else {
						emojiMaps.add(new EmojiMap(words[0], category, null,
								words[1]));
						Log.v(TAG + ".loadConfiguration", words[0] + " "
								+ EMOJI_CATEGORIES[category] + " " + words[1]);
					}
				} else if (words.length == 3) {
					if (isImageName(words[1])) {
						emojiMaps.add(new EmojiMap(words[0], category,
								words[1], words[2]));
						Log.v(TAG + ".loadConfiguration", words[0] + " "
								+ EMOJI_CATEGORIES[category] + " " + words[1]
								+ " " + words[2]);
					} else {
						emojiMaps.add(new EmojiMap(words[0], category,
								words[2], words[1]));
						Log.v(TAG + ".loadConfiguration", words[0] + " "
								+ EMOJI_CATEGORIES[category] + " " + words[2]
								+ " " + words[1]);
					}
				} else {
					Log.e(TAG, "EmojiConfiguration File Error at line "
							+ (emojiMaps.size() + 1) + " of file "
							+ mBasedFolder + "/map_"
							+ EMOJI_CATEGORIES[category] + ".txt");
				}
			}

			Log.v(TAG, "Load Configuration Done");
		} catch (IOException e) {
			Log.v(TAG, "Can not find category: " + EMOJI_CATEGORIES[category]);
			return;
		}
	}

	public Emoji getEmoji(Context context, String code, int category, boolean isAnimated) {

		//return new Emoji(context, code, category, isAnimated, mImageGetter.getDrawable(code, category, isAnimated), mImageGetter.getName(code, category));
		return new Emoji(context, code, category, isAnimated, mImageGetter.getDrawableFromResource(context, code, category), mImageGetter.getName(code, category));
	}
	private static boolean isImageName(String name) {

		return name.matches(".+\\.png") || name.matches(".+\\.jpg")
				|| name.matches(".+\\.gif") || name.matches(".+\\.bmp");
	}

	public static void insertSpannedTextToCurrentPosition(Spanned textToInsert,
			EditText currentView, int start, int end) {

		currentView.getText().replace(start, end, textToInsert, 0,
				textToInsert.length());
	}

	public static void copySpannedTextToClipboard(Spanned selectedText,
			ClipboardManager clipboardManager) {
		// remove <p></p> pair and \n character then copy to clipboard
		clipboardManager.setText(Html.toHtml(selectedText).replaceAll(
				"</?p>|\n", ""));
		Log.v(TAG, "Text copied: " + clipboardManager.getText());
	}

	public String toHtml(final String unicode) {
		String content = new String(unicode);
		for (ArrayList<EmojiMap> map : maps) {
			for (EmojiMap emojiMaps : map) {
				content = content.replaceAll(emojiMaps.code, "<img src='"
						+ emojiMaps.code + "' />");
			}
		}
		// Remove all return char to <br>
		String html = content.replaceAll("\n", "<br/>");
		return html;
	}

	/**
	 * Convert html code to unicode
	 * 
	 * @param html
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public String toUnicode(final String html)
			throws UnsupportedEncodingException {
		String content = new String(html);
		for (ArrayList<EmojiMap> map : maps) {
			for (EmojiMap emojiMaps : map) {
				content = content.replaceAll("<img src=\"" + emojiMaps.code
						+ "\">", URLEncoder.encode(emojiMaps.code, "UTF-8"));
			}
		}
		// fix line feed for compatible iphone and android
		//content = content.replaceAll("<br>", "\n");
		//content = content.replaceAll("<br/>", "\n");
		// convert to unicode
		Document doc = Jsoup.parse(content);
		HtmlToPlainText formatter = new HtmlToPlainText();
		String unicode = formatter.getPlainText(doc);
		// fix for jsoup utf8 bug
		unicode = URLDecoder.decode(unicode, "UTF-8");
		return unicode;
	}

	/**
	 * Return the spanned text contain emoji
	 * 
	 * @param decodedString
	 *            string contain emoji after decoded
	 * @return The spanned text contain emoji
	 */
	public Spanned toEmojiText(String decodedString) {
		return Html.fromHtml(toHtml(decodedString), mImageGetter, null);
	}

	/**
	 * Convert to unicode string from emoji text
	 * 
	 * @param emojiText
	 * @return unicode string
	 */
	public String toUnicode(Spanned emojiText) {
		String unicode = "";
		try {
			unicode = toUnicode(Html.toHtml(emojiText));
		} catch (Exception e) {
			// TODO: handle exception
		}
		return unicode;
	}

	public String getBaseFolder() {
		return mBasedFolder;
	}

	public ArrayList<EmojiMap>[] getEmojiMaps() {
		return maps;
	}

	public EmojiDrawableGetter getImageGetter() {
		return mImageGetter;
	}
}
